package visualgeditor.editor.folding;

import java.util.ArrayList;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
import org.eclipse.swt.widgets.Display;

import visualgeditor.editor.VisualgEditor;

public class VisualgReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension {

	private VisualgEditor editor;

	private IDocument fDocument;

	/** holds the calculated positions */
	@SuppressWarnings("rawtypes")
	protected final ArrayList fPositions = new ArrayList();

	/** The offset of the next character to be read */
	protected int fOffset;

	/** The end offset of the range to be scanned */
	protected int fRangeEnd;

	/**
	 * @return Returns the editor.
	 */
	public VisualgEditor getEditor() {
		return editor;
	}

	public void setEditor(VisualgEditor editor) {
		this.editor = editor;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
	 */
	public void setDocument(IDocument document) {
		this.fDocument = document;

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion,
	 *      org.eclipse.jface.text.IRegion)
	 */
	public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
		initialReconcile();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
	 */
	public void reconcile(IRegion partition) {
		initialReconcile();
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
	 */
	public void setProgressMonitor(IProgressMonitor monitor) {
		// TODO Auto-generated method stub

	}

	/*
	 * (non-Javadoc)
	 *
	 * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile()
	 */
	public void initialReconcile() {
		fOffset = 0;
		fRangeEnd = fDocument.getLength();
		calculatePositions();

	}

	/**
	 * next character position - used locally and only valid while
	 * {@link #calculatePositions()} is in progress.
	 */
	protected int cNextPos = 0;

	/** number of newLines found by {@link #classifyTag()} */
	protected int cNewLines = 0;

	protected char cLastNLChar = ' ';

	/**
	 * uses {@link #fDocument}, {@link #fOffset} and {@link #fRangeEnd} to
	 * calculate {@link #fPositions}. About syntax errors: this method is not a
	 * validator, it is useful.
	 */
	protected void calculatePositions() {
		fPositions.clear();
		cNextPos = fOffset;

		try {
			recursiveTokens(0);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}

		Display.getDefault().asyncExec(new Runnable() {
			public void run() {
				editor.updateFoldingStructure(fPositions);
			}

		});
	}

	/**
	 * emits tokens to {@link #fPositions}.
	 *
	 * @return number of newLines
	 * @throws BadLocationException
	 */
	protected int recursiveTokens(int depth) throws BadLocationException {
		int newLines = 0;
		String token = "";
		while (cNextPos < fRangeEnd) {
			char ch = fDocument.getChar(cNextPos++);
			token += ch;

			if (ch == '\n' || ch == ' ' || ch == '\r' || 
					token.trim().startsWith("\"") || 
					token.trim().startsWith("//"))
			{
				if (token.trim().startsWith("//") || token.trim().contains("\""))
				{
					while (ch != '\n')
					{
						ch = fDocument.getChar(cNextPos++);
					}
				}
				else if (token.trim().equals("algoritmo") || token.trim().equals("funcao") || token.trim().equals("procedimento")
						|| token.trim().equals("fimalgoritmo") || token.trim().equals("fimfuncao") ||
						token.trim().equals("fimprocedimento"))
				{
					if (token.trim().equals("fimalgoritmo") || token.trim().equals("fimfuncao") ||
							token.trim().equals("fimprocedimento"))
					{
						cNewLines += eatToEndOfLine();
						cNextPos--;
						return newLines;
					}
					if(depth <= 1)
					{
						int startOffset = cNextPos - 1;
						int startNewLines = newLines;
						newLines += recursiveTokens(depth + 1);
						if (newLines > startNewLines+1) {
							emitPosition(startOffset, cNextPos - startOffset);
						}
					}

				}

				else if (token.trim().equals("fimpara") || token.trim().equals("fimse") ||
						token.trim().equals("fimenquanto") || token.trim().equals("fimescolha") || token.trim().equals("fimrepita")
						|| token.trim().equals("para") || token.trim().equals("enquanto") || token.trim().equals("se") ||
						token.trim().equals("escolha") || token.trim().equals("repita") || token.trim().equals("ate"))
				{
					if(depth >= 1){
						
						if (token.trim().equals("para"))
						{
							int startOffset = cNextPos - 1;
							int startNewLines = newLines;
							newLines += recursiveTokens(depth + 1);
							if (newLines > startNewLines+1) {
								emitPosition(startOffset, cNextPos - startOffset);
							}
						}
						
						else if (token.trim().equals("ate"))
						{
							int oldNextPosition = cNextPos;
							char c = fDocument.getChar(oldNextPosition++);
							String faca = "";
							while (c != '\n')
							{
								faca += c;
								c = fDocument.getChar(oldNextPosition++);
							}
							if(!(faca.contains("faca")))
							{
								cNextPos = oldNextPosition;
								cNewLines += eatToEndOfLine();
								cNextPos--;
								return newLines;
							}
						}
						
						else if (token.trim().equals("fimpara") || token.trim().equals("fimse") ||
								token.trim().equals("fimenquanto") || token.trim().equals("fimescolha") || token.trim().equals("fimrepita"))
						{
							cNewLines += eatToEndOfLine();
							cNextPos--;
							return newLines;
						}
						else
						{
							int startOffset = cNextPos - 1;
							int startNewLines = newLines;
							newLines += recursiveTokens(depth + 1);
							if (newLines >= startNewLines+1) {
								emitPosition(startOffset, cNextPos - startOffset);
							}
						}
				}
			}

//			else if (token.trim().equals("para") || token.trim().equals("enquanto") || token.trim().equals("se") ||
//					token.trim().equals("escolha") || token.trim().equals("repita"))
//			{
//
//				if(depth >= 1){
//					int startOffset = cNextPos - 1;
//					int startNewLines = newLines;
//					newLines += recursiveTokens(depth + 1);
//					if (newLines > startNewLines+1) {
//						emitPosition(startOffset, cNextPos - startOffset);
//					}
//				}
//			}

			switch (ch)
			{
			case '\n':
			case '\r':
				newLines++;
				break;
			default:
				break;
			}

			token = "";
		}
	}

	return newLines;
}

@SuppressWarnings("unchecked")
protected void emitPosition(int startOffset, int length) {
	fPositions.add(new Position(startOffset, length));
}

protected int eatToEndOfLine() throws BadLocationException {
	if (cNextPos >= fRangeEnd) {
		return 0;
	}
	char ch = fDocument.getChar(cNextPos++);

	while ((cNextPos < fRangeEnd) && ((' ' == ch) || ('\t' == ch))) {
		ch = fDocument.getChar(cNextPos++);
	}
	if (cNextPos >= fRangeEnd) {
		cNextPos--;
		return 0;
	}

	if ('\n' == ch) {
		if (cNextPos < fRangeEnd) {
			ch = fDocument.getChar(cNextPos++);
			if ('\r' != ch) {
				cNextPos--;
			}
		} else {
			cNextPos--;
		}
		return 1;
	}

	if ('\r' == ch) {
		if (cNextPos < fRangeEnd) {
			ch = fDocument.getChar(cNextPos++);
			if ('\n' != ch) {
				cNextPos--;
			}
		} else {
			cNextPos--;
		}
		return 1;
	}

	return 0;
}
}